},
launch : function() {
- // While using Gio.app_info_launch_default_for_uri() would be
- // shorter in terms of lines of code, we are not doing so
- // because that would duplicate the work of retrieving the
- // mime type.
- let needsUri = Gio.file_new_for_uri(this.uri).get_path() == null;
- let appInfo = Gio.app_info_get_default_for_type(this.mimeType, needsUri);
-
- if (appInfo != null) {
- appInfo.launch_uris([this.uri], Main.createAppLaunchContext());
- } else {
- log("Failed to get default application info for mime type " + this.mimeType +
- ". Will try to use the last application that registered the document.");
- let appName = this.recentInfo.last_application();
- let [success, appExec, count, time] = this.recentInfo.get_application_info(appName);
- if (success) {
- log("Will open a document with the following command: " + appExec);
- // TODO: Change this once better support for creating
- // GAppInfo is added to GtkRecentInfo, as right now
- // this relies on the fact that the file uri is
- // already a part of appExec, so we don't supply any
- // files to appInfo.launch().
-
- // The 'command line' passed to
- // create_from_command_line is allowed to contain
- // '%<something>' macros that are expanded to file
- // name / icon name, etc, so we need to escape % as %%
- appExec = appExec.replace(/%/g, "%%");
-
- let appInfo = Gio.app_info_create_from_commandline(appExec, null, 0, null);
-
- // The point of passing an app launch context to
- // launch() is mostly to get startup notification and
- // associated benefits like the app appearing on the
- // right desktop; but it doesn't really work for now
- // because with the way we create the appInfo we
- // aren't reading the application's desktop file, and
- // thus don't find the StartupNotify=true in it. So,
- // despite passing the app launch context, no startup
- // notification occurs.
- appInfo.launch([], Main.createAppLaunchContext());
- } else {
- log("Failed to get application info for " + this.uri);
- }
- }
+ Shell.DocSystem.get_default().open(this.recentInfo);
},
exists : function() {
return docManagerInstance;
}
+/**
+ * DocManager wraps the DocSystem, primarily to expose DocInfo objects
+ * which conform to the GenericDisplay item API.
+ */
function DocManager() {
this._init();
}
DocManager.prototype = {
_init: function() {
- this._recentManager = Gtk.RecentManager.get_default();
- this._items = {};
- this._recentManager.connect('changed', Lang.bind(this, function(recentManager) {
- this._reload();
- this.emit('changed');
- }));
+ this._docSystem = Shell.DocSystem.get_default();
+ this._infosByTimestamp = [];
+ this._infosByUri = {};
+ this._docSystem.connect('changed', Lang.bind(this, this._reload));
this._reload();
},
_reload: function() {
- let docs = this._recentManager.get_items();
- let newItems = {};
+ let docs = this._docSystem.get_all();
+ this._infosByTimestamp = [];
+ this._infosByUri = {};
for (let i = 0; i < docs.length; i++) {
let recentInfo = docs[i];
- if (!recentInfo.exists())
- continue;
let docInfo = new DocInfo(recentInfo);
-
- // we use GtkRecentInfo URI as an item Id
- newItems[docInfo.uri] = docInfo;
- }
- let deleted = {};
- for (var uri in this._items) {
- if (!(uri in newItems))
- deleted[uri] = this._items[uri];
+ this._infosByTimestamp.push(docInfo);
+ this._infosByUri[docInfo.uri] = docInfo;
}
- /* If we'd cached any thumbnail references that no longer exist,
- dump them here */
- let texCache = Shell.TextureCache.get_default();
- for (var uri in deleted) {
- texCache.evict_recent_thumbnail(this._items[uri].recentInfo);
- }
- this._items = newItems;
+ this.emit('changed');
+ },
+
+ getTimestampOrderedInfos: function() {
+ return this._infosByTimestamp;
+ },
+
+ getInfosByUri: function() {
+ return this._infosByUri;
+ },
+
+ lookupByUri: function(uri) {
+ return this._infosByUri[uri];
},
- getItems: function() {
- return this._items;
+ queueExistenceCheck: function(count) {
+ return this._docSystem.queue_existence_check(count);
}
}
_refreshCache : function() {
if (!this._docsStale)
return true;
- this._allItems = this._docManager.getItems();
+ this._allItems = {};
+ Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
this._docsStale = false;
return false;
},
Main.overview.hide();
}));
+ this.actor._delegate = this;
+
this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
- let name = new St.Label({ style_class: "dash-recent-docs-item",
+ let name = new St.Label({ style_class: 'dash-recent-docs-item',
text: docInfo.name });
this.actor.append(name, Big.BoxPackFlags.EXPAND);
let draggable = DND.makeDraggable(this.actor);
- this.actor._delegate = this;
+ },
+
+ getUri: function() {
+ return this._info.uri;
},
getDragActorSource: function() {
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
+ this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
+
+ this._actorsByUri = {};
this._docManager = DocInfo.getDocManager();
- this._docManager.connect('changed', Lang.bind(this, function(mgr) {
- this._redisplay();
- }));
- this._redisplay();
+ this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
+ this._pendingDocsChange = true;
+ this._checkDocExistence = false;
},
_getPreferredWidth: function(actor, forHeight, alloc) {
let firstColumnChildren = Math.ceil(children.length / 2);
+ let natural = 0;
for (let i = 0; i < firstColumnChildren; i++) {
let child = children[i];
- let [minSize, naturalSize] = child.get_preferred_height(forWidth);
- alloc.natural_size += naturalSize;
+ let [minSize, naturalSize] = child.get_preferred_height(-1);
+ natural += naturalSize;
if (i > 0 && i < children.length - 1) {
- alloc.natural_size += DEFAULT_SPACING;
+ natural += DEFAULT_SPACING;
}
}
+ alloc.natural_size = natural;
},
_allocate: function(actor, box, flags) {
i++;
}
- // Everything else didn't fit, just hide it.
- for (; i < children.length; i++) {
- children[i].hide();
+ if (this._checkDocExistence) {
+ // Now we know how many docs we are displaying, queue a check to see if any of them
+ // have been deleted. If they are deleted, then we'll get a 'changed' signal; since
+ // we'll now be displaying items we weren't previously, we'll check again to see
+ // if they were deleted, and so forth and so on.
+ // TODO: We should change this to ask for as many as we can fit in the given space:
+ // https://bugzilla.gnome.org/show_bug.cgi?id=603522#c23
+ this._docManager.queueExistenceCheck(i);
+ this._checkDocExistence = false;
}
+
+ let skipPaint = [];
+ for (; i < children.length; i++)
+ this.actor.set_skip_paint(children[i], true);
+ },
+
+ _onDocsChanged: function() {
+ this._checkDocExistence = true;
+ Main.queueDeferredWork(this._workId);
},
_redisplay: function() {
+ // Should be kept alive by the _actorsByUri
this.actor.remove_all();
-
- let docs = this._docManager.getItems();
- let docUrls = [];
- for (let url in docs) {
- docUrls.push(url);
+ let docs = this._docManager.getTimestampOrderedInfos();
+ for (let i = 0; i < docs.length; i++) {
+ let doc = docs[i];
+ let display = this._actorsByUri[doc.uri];
+ if (display) {
+ this.actor.add_actor(display.actor);
+ } else {
+ let display = new DashDocDisplayItem(doc);
+ this.actor.add_actor(display.actor);
+ this._actorsByUri[doc.uri] = display;
+ }
}
- docUrls.sort(function (urlA, urlB) { return docs[urlB].timestamp - docs[urlA].timestamp; });
- let textureCache = Shell.TextureCache.get_default();
-
- for (let i = 0; i < docUrls.length; i++) {
- let url = docUrls[i];
- let docInfo = docs[url];
- let display = new DashDocDisplayItem(docInfo);
- this.actor.add_actor(display.actor);
+ // Any unparented actors must have been deleted
+ for (let uri in this._actorsByUri) {
+ let display = this._actorsByUri[uri];
+ if (display.actor.get_parent() == null) {
+ display.actor.destroy();
+ delete this._actorsByUri[uri];
+ }
}
this.emit('changed');
}